home *** CD-ROM | disk | FTP | other *** search
/ Delphi Developer's Kit 1996 / Delphi Developer's Kit 1996.iso / power / numedit / numedit.pas next >
Pascal/Delphi Source File  |  1995-12-22  |  11KB  |  369 lines

  1. { Newsgroups: comp.lang.pascal.delphi.components
  2. Subject: Number Editing Component
  3. From: root@comu2.auckland.ac.nz (Richard Vowles)
  4. Date: 15 Aug 1995 20:04:18 GMT
  5.  
  6. Some people have expressed a need for this, I certainly have
  7. needed it! If anyone wants to put it on an ftp server feel welcome!
  8. }
  9.  
  10.  
  11. (*
  12. ** NumEdit Version 1.0, Released 15/08/95
  13. **
  14. ** This component, Numedit is an expansion of the initial work on
  15. ** CurrencyEdit done by Robert Vivrette (of the Unofficial Newsletter
  16. ** of Delphi Users, a most excellent set of help files).
  17. **
  18. ** It fixes three bugs that I know of in that code:
  19. ** the locations of the - and the . - which
  20. ** allowed the user to have a - anywhere they liked, and multiple
  21. ** decimal .'s, and the ability to use the delete and backspace keys.
  22. **
  23. ** It also adds facilities for entering integers, long
  24. ** integers, and words, restricting users to what they are actually
  25. ** allowed to enter for those values, so you will always get a valid
  26. ** integer/longint/word/floatingpoint value back.
  27. **
  28. ** This work is copyrighted by Richard Vowles, r.vowles@auckland.ac.nz.
  29. ** You can use it as you like it, you can publish as part of freeware
  30. ** collections and so forth. What you cannot do is take it am claim it
  31. ** as your own and sell it as part of a collection of your own work.
  32. ** Oh, and you must leave this entire text at the top of the unit
  33. ** declaration (which tells you that Robert did the start, and I did
  34. ** the rest).
  35. **
  36. ** I would be most interested if anyone finds bugs in it that can be
  37. ** fixed. It is part of a more type-aware grid that I am working on
  38. ** y'see...
  39. **
  40. ** The majority of the code added is in Keypress, but there are other
  41. ** bits scattered around.
  42. *)
  43.  
  44. Unit numedit;
  45.  
  46. Interface
  47.  
  48. uses
  49.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  50.   Menus, Forms, Dialogs, StdCtrls;
  51.  
  52. type (* EM_LINEINDEX *)
  53.   TNumberEditType = (integervalue, longintvalue, floatingpointvalue, wordvalue);
  54.  
  55.   TNumberEdit = class(TCustomMemo)
  56.   private
  57.     FNumberEditType : TNumberEditType;
  58.     DispFormat: string;
  59.     FieldValue: Extended;
  60.     procedure SetFormat(A: string);
  61.     procedure SetFieldValue(A: Extended);
  62.     procedure CMEnter(var Message: TCMEnter);           message CM_ENTER;
  63.     procedure CMExit(var Message: TCMExit);             message CM_EXIT;
  64.     procedure FormatText;
  65.     procedure UnFormatText;
  66.     procedure SetDefaultDisplay;
  67.     procedure SetNumberEditType( newtype : TNumberEditType );
  68.   protected
  69.     procedure KeyPress(var Key: Char); override;
  70.     procedure CreateParams(var Params: TCreateParams); override;
  71.   public
  72.     constructor Create(AOwner: TComponent); override;
  73.   published
  74.     property Alignment default taRightJustify;
  75.     (*property AutoSize default True;*)
  76.     property BorderStyle;
  77.  
  78.     property EntryRequired : TNumberEditType read FNumberEditType write SetNumberEditType;
  79.     property Color;
  80.     property Ctl3D;
  81.     property DisplayFormat: string read DispFormat write SetFormat;
  82.     property DragCursor;
  83.     property DragMode;
  84.     property Enabled;
  85.     property Font;
  86.     property HideSelection;
  87.     property MaxLength;
  88.     property ParentColor;
  89.     property ParentCtl3D;
  90.     property ParentFont;
  91.     property ParentShowHint;
  92.     property PopupMenu;
  93.     property ReadOnly;
  94.     property ShowHint;
  95.     property TabOrder;
  96.     property Value: Extended read FieldValue write SetFieldValue;
  97.     property Visible;
  98.     property OnChange;
  99.  
  100.     property OnClick;
  101.     property OnDblClick;
  102.     property OnDragDrop;
  103.     property OnDragOver;
  104.     property OnEndDrag;
  105.     property OnEnter;
  106.     property OnExit;
  107.     property OnKeyDown;
  108.     property OnKeyPress;
  109.     property OnKeyUp;
  110.     property OnMouseDown;
  111.     property OnMouseMove;
  112.     property OnMouseUp;
  113.   end;
  114.  
  115. procedure Register;
  116.  
  117. implementation
  118.  
  119. procedure Register;
  120. begin
  121.   RegisterComponents('Samples', [TNumberEdit]);
  122. end;
  123. procedure TNumberEdit.SetNumberEditType( newtype : TNumberEditType );
  124. begin
  125.      FNumberEditType := newtype;
  126.      SetDefaultDisplay;
  127. end;
  128.  
  129. procedure TNumberEdit.SetDefaultDisplay;
  130. begin
  131.      case FNumberEditType of
  132.        integervalue:
  133.          DispFormat := '0;-0';
  134.        longintvalue:
  135.          DispFormat := '0;-0';
  136.        wordvalue:
  137.          DispFormat := '0;-0';
  138.        floatingpointvalue:
  139.          DispFormat := '$,0.00;($,0.00)';
  140.      end;
  141.  
  142.      FormatText;
  143. end;
  144.  
  145. constructor TNumberEdit.Create(AOwner: TComponent);
  146. begin
  147.   inherited Create(AOwner);
  148.  
  149.   AutoSize := False;       (* allowing this causes some funny stuff! *)
  150.   FNumberEditType := floatingpointvalue;
  151.   Alignment := taRightJustify;
  152.   Width := 121;
  153.   Height := 25;
  154.  
  155.   SetDefaultDisplay;
  156.  
  157.   FieldValue := 0.0;
  158.   AutoSelect := False;
  159.   WantReturns := False;
  160.   WordWrap := False;
  161.   FormatText;
  162. end;
  163.  
  164. procedure TNumberEdit.SetFormat(A: String);
  165. begin
  166.   if DispFormat <> A then
  167.     begin
  168.       DispFormat:= A;
  169.       FormatText;
  170.     end;
  171. end;
  172.  
  173. procedure TNumberEdit.SetFieldValue(A: Extended);
  174. begin
  175.   if FieldValue <> A then
  176.     begin
  177.       FieldValue := A;
  178.       FormatText;
  179.     end;
  180. end;
  181.  
  182. procedure TNumberEdit.UnFormatText;
  183. var
  184.   TmpText : String;
  185.   Tmp     : Byte;
  186.   IsNeg   : Boolean;
  187. begin
  188.   IsNeg := (Pos('-',Text) > 0) or (Pos('(',Text) > 0);
  189.   TmpText := '';
  190.   For Tmp := 1 to Length(Text) do
  191.  
  192.     if Text[Tmp] in ['0'..'9','.'] then
  193.       TmpText := TmpText + Text[Tmp];
  194.   try
  195.     if length(TmpText) = 0 then
  196.        FieldValue := 0.0
  197.     else
  198.        FieldValue := StrToFloat(TmpText);
  199.     if IsNeg then FieldValue := -FieldValue;
  200.   except
  201.     MessageBeep(mb_IconAsterisk);
  202.   end;
  203. end;
  204.  
  205. procedure TNumberEdit.FormatText;
  206. begin
  207.   Text := FormatFloat(DispFormat,FieldValue);
  208. end;
  209.  
  210. procedure TNumberEdit.CMEnter(var Message: TCMEnter);
  211. begin
  212.   SelectAll;
  213.   inherited;
  214. end;
  215.  
  216. procedure TNumberEdit.CMExit(var Message: TCMExit);
  217. begin
  218.   UnformatText;
  219.   FormatText;
  220.   Inherited;
  221. end;
  222.  
  223. procedure TNumberEdit.KeyPress(var Key: Char);
  224. var
  225.    posOf      : longint;
  226.    pos1, pos2 : word;
  227.    ok         : Boolean;
  228.    isneg      : Boolean;
  229.    val        : string;
  230.    toinsert   : string[1];
  231. begin
  232.   case FNumberEditType of
  233.    integervalue:
  234.      ok := key in ['0'..'9','-', #8, #127];
  235.    longintvalue:
  236.      ok := key in ['0'..'9','-', #8, #127];
  237.    wordvalue:
  238.      ok := key in ['0'..'9', #8, #127];
  239.    floatingpointvalue:
  240.      ok := Key in ['0'..'9','.','-', #8, #127];
  241.   end;
  242.  
  243.   if Not ok Then
  244.      Key := #0
  245.   else
  246.      begin  (* both of the following if's need it *)
  247.      posOf := SendMessage( Self.Handle, EM_GETSEL, 0, 0 );
  248.      pos1 := posOf AND $FFFF;
  249.      pos2 := posOf SHR 16;
  250.      end;
  251.  
  252.    if (Key = '-') or (Key = '.') then
  253.      begin
  254.      if (Key = '-') and ( pos1 <> 0 ) then (* ie the char won't go into pos1 *)
  255.         Key := #0
  256.      else if ( Key = '.' ) then
  257.        begin
  258.        if pos('.', Text) > 0 then (* there is already a . in this line *)
  259.          begin
  260.          if pos1 <> pos2 then
  261.            begin (* get the selected text and see if the . is in it (and
  262.                  ** will thus be replaced
  263.                  *)
  264.            if pos('.', SelText) = 0 then
  265.              Key := #0;
  266.            end
  267.          else
  268.            Key := #0;
  269.          end;
  270.        end;
  271.      end
  272.   else if ok and (Key <> #8) and (Key <> #127) then
  273.       begin
  274.       (*
  275.       ** first we need to ensure that the character isn't being
  276.       ** inserted before a - sign (which is not allowed)
  277.       *)
  278.       if pos('-', SelText) = 0 then (* they are not replacing it *)
  279.         if (pos1 = 0) and (Text[1] = '-') then
  280.           Key := #0;
  281.       (*
  282.       ** we have to ensure that the number is not too big for
  283.       ** the value it will eventually go into. The only way we
  284.       ** can do this is to figure out what it would be like if
  285.       ** the key press goes thru (as we have to deal with selected
  286.       ** text as well) and then decide as to whether it is legitimate
  287.       ** or not.
  288.       *)
  289.       if key <> #0 then
  290.         begin
  291.         val := Text;
  292.  
  293.         toinsert[0] := #1;
  294.         toinsert[1] := Key;
  295.  
  296.         if pos1 <> pos2 then (* something was selected *)
  297.           begin
  298.           Delete( val, pos1 + 1, pos2 - pos1 );
  299.           end;
  300.  
  301.         Insert( toinsert, val, pos1 + 1 );
  302.         (*
  303.         ** now we have a representation of what the string WILL be
  304.         ** We can determine if it is valid. For integer and word this
  305.         ** is easy as we just convert it to an integer and check if
  306.         ** is within the bounds. For longints it is more difficult.
  307.         *)
  308.         ok := True;
  309.  
  310.         case FNumberEditType of
  311.           integervalue:
  312.             begin
  313.             if val <> '-' then
  314.               begin
  315.               posOf := StrToInt( val );
  316.               if (posOf < -32768) or (posOf > 32767) then
  317.                 ok := False;
  318.               end;
  319.             end;
  320.           wordvalue:
  321.             begin
  322.             posOf := StrToInt( val );
  323.             if (posOf > 65535) then (* can't be < 0, no - allowed *)
  324.                ok := False;
  325.             end;
  326.        longintvalue:
  327.             begin
  328.             if val[1] = '-' then
  329.                begin
  330.                Delete(val,1,1);
  331.                if ((length(val) = 10) and (val > '2147483648')) or (length(val) > 10 ) then
  332.                     ok := False;
  333.                end
  334.             else
  335.                begin
  336.                if ((length(val) = 10) and (val > '2147483647')) or (length(val) > 10 ) then
  337.                     ok := False;
  338.                end;
  339.             end;
  340.           end; (* case *)
  341.  
  342.         if not ok then
  343.           Key := #0;
  344.         end; (* if they weren't inserting a num before a - *)
  345.       end;
  346.      (* The Keypress .
  347.      ** The . can occur anywhere in the text, but it can only occur
  348.      ** ONCE. We could check to see if it were already there, but it
  349.      ** is complicated by the fact that if they have selected text
  350.      ** which has one (.) in it and they wish to hit the . then
  351.      *)
  352.   inherited KeyPress(Key);
  353. end;
  354.  
  355. procedure TNumberEdit.CreateParams(var Params: TCreateParams);
  356.  
  357. begin
  358.   inherited CreateParams(Params);
  359.   case Alignment of
  360.     taLeftJustify  : Params.Style := Params.Style or ES_LEFT and Not ES_MULTILINE;
  361.     taRightJustify : Params.Style := Params.Style or ES_RIGHT and Not ES_MULTILINE;
  362.     taCenter       : Params.Style := Params.Style or ES_CENTER and Not ES_MULTILINE;
  363.   end;
  364. end;
  365.  
  366. End.
  367.  
  368.  
  369.